Bun WebSocket Server
Bun has built-in WebSocket support integrated with
Bun.serve()
.
Quick Start
const
server
=
Bun
.
serve
(
{
fetch
(
req
,
server
)
{
// Upgrade to WebSocket
if
(
server
.
upgrade
(
req
)
)
{
return
;
// Upgraded successfully
}
return
new
Response
(
"Not a WebSocket request"
,
{
status
:
400
}
)
;
}
,
websocket
:
{
open
(
ws
)
{
console
.
log
(
"Client connected"
)
;
}
,
message
(
ws
,
message
)
{
console
.
log
(
"Received:"
,
message
)
;
ws
.
send
(
Echo:
${
message
}
)
;
}
,
close
(
ws
)
{
console
.
log
(
"Client disconnected"
)
;
}
,
}
,
}
)
;
console
.
log
(
WebSocket server running on ws://localhost:
${
server
.
port
}
)
;
WebSocket Handlers
Bun
.
serve
(
{
fetch
(
req
,
server
)
{
server
.
upgrade
(
req
)
;
}
,
websocket
:
{
// Client connected
open
(
ws
)
{
console
.
log
(
"New connection"
)
;
}
,
// Message received
message
(
ws
,
message
)
{
// message is string | Buffer
if
(
typeof
message
===
"string"
)
{
console
.
log
(
"Text:"
,
message
)
;
}
else
{
console
.
log
(
"Binary:"
,
message
)
;
}
}
,
// Connection closed
close
(
ws
,
code
,
reason
)
{
console
.
log
(
Closed:
${
code
}
-
${
reason
}
)
;
}
,
// Drain event (buffer flushed)
drain
(
ws
)
{
console
.
log
(
"Buffer drained"
)
;
}
,
// Ping received
ping
(
ws
,
data
)
{
// Pong sent automatically
}
,
// Pong received
pong
(
ws
,
data
)
{
console
.
log
(
"Pong received"
)
;
}
,
}
,
}
)
;
Sending Messages
websocket
:
{
message
(
ws
,
message
)
{
// Send text
ws
.
send
(
"Hello"
)
;
// Send JSON
ws
.
send
(
JSON
.
stringify
(
{
type
:
"greeting"
,
data
:
"Hello"
}
)
)
;
// Send binary
ws
.
send
(
new
Uint8Array
(
[
1
,
2
,
3
]
)
)
;
ws
.
send
(
Buffer
.
from
(
"binary data"
)
)
;
// Send with compression
ws
.
send
(
"compressed message"
,
true
)
;
// Check if buffer is full
const
bufferedAmount
=
ws
.
send
(
"data"
)
;
if
(
bufferedAmount
1024 * 1024 ) { console . log ( "Buffer getting full" ) ; } } , } Attaching Data to Connections interface UserData { id : string ; name : string ; joinedAt : Date ; } Bun . serve < UserData
( { fetch ( req , server ) { const url = new URL ( req . url ) ; const userId = url . searchParams . get ( "userId" ) ; // Attach data during upgrade server . upgrade ( req , { data : { id : userId , name : "User " + userId , joinedAt : new Date ( ) , } , } ) ; } , websocket : { open ( ws ) { // Access attached data console . log (
${ ws . data . name } connected) ; } , message ( ws , message ) { console . log (${ ws . data . name } : ${ message }) ; } , } , } ) ; Pub/Sub (Topics) Bun . serve ( { fetch ( req , server ) { const url = new URL ( req . url ) ; const room = url . searchParams . get ( "room" ) || "general" ; server . upgrade ( req , { data : { room } , } ) ; } , websocket : { open ( ws ) { // Subscribe to a topic ws . subscribe ( ws . data . room ) ; // Publish to topic (excludes sender) ws . publish ( ws . data . room ,User joined ${ ws . data . room }) ; } , message ( ws , message ) { // Broadcast to all in room (excludes sender) ws . publish ( ws . data . room , message ) ; } , close ( ws ) { // Unsubscribe (automatic on close) ws . unsubscribe ( ws . data . room ) ; ws . publish ( ws . data . room , "User left" ) ; } , } , } ) ; Broadcasting to All Clients Bun . serve ( { fetch ( req , server ) { server . upgrade ( req ) ; } , websocket : { open ( ws ) { // Subscribe to global topic ws . subscribe ( "global" ) ; } , message ( ws , message ) { // Broadcast to ALL clients including sender server . publish ( "global" , message ) ; } , } , } ) ; Server-Level Publish const server = Bun . serve ( { fetch ( req , server ) { const url = new URL ( req . url ) ; // HTTP endpoint to publish if ( url . pathname === "/broadcast" ) { const message = url . searchParams . get ( "msg" ) ; server . publish ( "global" , message ) ; return new Response ( "Broadcasted" ) ; } server . upgrade ( req ) ; } , websocket : { open ( ws ) { ws . subscribe ( "global" ) ; } , } , } ) ; // Can also publish from outside fetch setInterval ( ( ) => { server . publish ( "global" ,Server time: ${ new Date ( ) . toISOString ( ) }) ; } , 5000 ) ; WebSocket Options Bun . serve ( { websocket : { // Max message size (default 16MB) maxPayloadLength : 1024 * 1024 , // 1MB // Idle timeout in seconds (default 120) idleTimeout : 60 , // Backpressure limit backpressureLimit : 1024 * 1024 , // Enable compression perMessageDeflate : true , // Or with options perMessageDeflate : { compress : "shared" , decompress : "shared" , } , // Send/receive pings sendPings : true , // Handlers open ( ws ) { } , message ( ws , message ) { } , close ( ws ) { } , } , } ) ; Client-Side Connection // Browser const ws = new WebSocket ( "ws://localhost:3000" ) ; ws . onopen = ( ) => { ws . send ( "Hello Server!" ) ; } ; ws . onmessage = ( event ) => { console . log ( "Received:" , event . data ) ; } ; ws . onclose = ( ) => { console . log ( "Disconnected" ) ; } ; Authentication Bun . serve ( { fetch ( req , server ) { // Verify auth before upgrade const token = req . headers . get ( "Authorization" ) ; if ( ! verifyToken ( token ) ) { return new Response ( "Unauthorized" , { status : 401 } ) ; } const user = decodeToken ( token ) ; server . upgrade ( req , { data : { userId : user . id } , } ) ; } , websocket : { open ( ws ) { console . log (Authenticated user ${ ws . data . userId } connected) ; } , } , } ) ; Common Errors Error Cause Fix Upgrade failed Invalid request Check upgrade headers Connection closed Client disconnect Handle in close handler Message too large Exceeds maxPayloadLength Increase limit or chunk data Backpressure Slow client Check buffer, wait for drain Common Patterns Chat Room Bun . serve ( { fetch ( req , server ) { const url = new URL ( req . url ) ; const username = url . searchParams . get ( "user" ) || "Anonymous" ; server . upgrade ( req , { data : { username } , } ) ; } , websocket : { open ( ws ) { ws . subscribe ( "chat" ) ; ws . publish ( "chat" ,${ ws . data . username } joined) ; } , message ( ws , message ) { ws . publish ( "chat" ,${ ws . data . username } : ${ message }) ; } , close ( ws ) { ws . publish ( "chat" ,${ ws . data . username } left) ; } , } , } ) ; When to Load References Load references/compression.md when: perMessageDeflate configuration Compression tuning Binary message handling Load references/scaling.md when: Multiple server instances Redis pub/sub integration Horizontal scaling